home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
ddj1190.arc
/
WILLIAMS.ASC
< prev
Wrap
Text File
|
1990-10-27
|
87KB
|
2,700 lines
_ROLL YOUR OWN DOS EXTENDER_
by Al Williams
[LISTING ONE]
;********************************************************************
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams All rights reserved. *
;* Permission is granted for non-commercial use of this software. *
;* You are expressly prohibited from selling this software, *
;* distributing it with another product, or removing this notice. *
;* If you distribute this software to others in any form, you must *
;* distribute all of the files that are listed below: *
;* PROT.ASM - The main routines and protected mode support. *
;* EQUMAC.INC - Equates and macros. *
;* STACKS.INC - Stack segments. *
;* GDT.INC - Global descriptor table. *
;* INT386.INC - Protected mode interrupt handlers. *
;* PMDEMO.PM - Example user code. *
;* PMPWD.PM - Alternate example code. *
;* FBROWSE.PM - Complete sample application. *
;* TSS.INC - Task state segments. *
;* CODE16.INC - 16 bit DOS code (entry/exit). *
;* PMASM.BAT - MASM driver for assembling PROT programs. *
;* To assemble: MASM /DPROGRAM=pname PROT.ASM,,PROT.LST; *
;* To link: LINK PROT; *
;* pname is the program name (code in pname.PM) *
;* if pname is ommited, USER.PM is used *
;* The resulting .EXE file is executable from the DOS prompt. *
;* This file is: PROT.ASM, the main protected mode code. *
;********************************************************************
.XLIST
.LALL
.386P
; Name program if PROGRAM is defined
IFDEF PROGRAM
VTITLE MACRO PNAME ; temporary macro to title program
TITLE PNAME
ENDM
VTITLE %PROGRAM
PURGE VTITLE ; delete macro
; equates and macros
INCLUDE EQUMAC.INC
; stack segments
INCLUDE STACKS.INC
; Global descriptor table definitons
INCLUDE GDT.INC
; interrupt code
INCLUDE INT386.INC
; this is required to find out how large PROT is
ZZZGROUP GROUP ZZZSEG
;********************************************************************
; 32 bit data segment
DAT32 SEGMENT PARA PUBLIC 'DATA32' USE32
DAT32BEG EQU $
; 32 bit stack values
SLOAD DD OFFSET SSEG321-1
SSLD DW SEL_STACK
; This location will hold the address for the PMODE IDT
NEWIDT EQU THIS FWORD
DW (IDTEND-IDTBEG)-1
IDTB DD 0 ; filled in at runtime
; PSP segment address
_PSP DW 0
; video variables for the OUCH and related routines
CURSOR DD 0 ; cursor location
COLOR DB 7 ; display cursor
; temp vars for some non reentrant interrupt routines
STO1 DD 0
STO2 DD 0
STO3 DD 0
STO4 DD 0
SAV_DS DD 0
SAV_ES DD 0
SAV_GS DD 0
SAV_FS DD 0
BPON DB 0 ; Enables conditional breakpoints
; Debug Dump variables
DUMP_SEG DW 0 ; if zero don't dump memory
DUMP_OFF DD 0 ; Offset to start at
DUMP_CNT DD 0 ; # of bytes to dump
; Break & critical error handler variables
BREAKKEY DB 0 ; break key occurred
CRITICAL DB 0 ; critical error occured
CRITAX DW 0 ; critical error ax
CRITDI DW 0 ; critical error di
CRITBP DW 0 ; critical error bp
CRITSI DW 0 ; critical error si
; Address of user's break handler
BREAK_HANDLE EQU THIS FWORD
BRK_OFF DD 0
BRK_SEG DW 0
; Address of user's critical error handler
CRIT_HANDLE EQU THIS FWORD
CRIT_OFF DD OFFSET DEF_CRIT
CRIT_SEG DW SEL_CODE32
; Message for default critical error handler
CRITMSG DB 'A critical error has occured.',13,10
DB '<A>bort, <R>etry, <F>ail? $'
; here is where vm86 int's stack up pl0 esp's
INTSP DD $+PVSTACK+4
DB PVSTACK DUP (0)
; Default VM86CALL parameter block
PINTFRAME VM86BLK <>
; interface block for critical error handler
CINTFRAME VM86BLK <>
; hardware interrupt vm86 block
HINTFRAME VM86BLK <>
; storage for the original PIC interrupt mask registers
INTMASK DB 0
INTMASKAT DB 0
DAT32END EQU $
DAT32 ENDS
;********************************************************************
; Begin 32 bit code segment
SEG32 SEGMENT PARA PUBLIC 'CODE32' USE32
ASSUME CS:SEG32, DS:DAT32
PCODE PROC
SEG32BEG EQU $
; Start of protected mode code. We jump here from inside CODE16.INC
SEG32ENT: MOV AX,SEL_DATA ; 1st order of business:
MOV DS,AX ; load up segment registers
LSS ESP, FWORD PTR SLOAD
MOV AX,SEL_VIDEO
MOV ES,AX
MOV AX,SEL_DATA0
MOV FS,AX
MOV AX,SEL_GDT
MOV GS,AX
; set up IDT
CALL32S MAKIDT
; reprogram pic(s)
IN AL,21H
MOV INTMASK,AL
IF ATCLASS
IN AL,0A1H
MOV INTMASKAT,AL
MOV AL,11H
OUT 0A0H,AL
OUT 20H,AL
IDELAY
MOV AL,28H
OUT 0A1H,AL
MOV AL,20H
OUT 21H,AL
IDELAY
MOV AL,2
OUT 0A1H,AL
MOV AL,4
OUT 21H,AL
IDELAY
MOV AL,1
OUT 0A1H,AL
OUT 21H,AL
IDELAY
MOV AL,INTMASKAT
OUT 0A1H,AL
MOV AL,INTMASK
OUT 21H,AL
ELSE
; INBOARD PC Code
MOV AL,13H
OUT 20H,AL
MOV AL,20H
OUT 21H,AL
MOV AL,9
OUT 21H,AL
MOV AL,INTMASK
OUT 21H,AL
ENDIF
STI ; enable interrupts
; *** Start user code with TSS (req'd for vm86 op's etc.)
MOV AX,TSS0
LTR AX
JMPABS32 TSS1,0
PCODE ENDP
;*** 32 bit support routines
; This routine creates the required IDT. This is only a subroutine to keep
; from cluttering up the main code, since you aren't likely to call it again.
; Assumes that all ISR routines are of fixed length and in sequence. After
; makidt has built the table, you can still replace individual INT gates with
; your own gates (see make_gate)
MAKIDT PROC NEAR
PUSH ES
MOV AX,IDTABLE
MOVZX EAX,AX
SHL EAX,4
ADD EAX,OFFSET IDTBEG
MOV IDTB,EAX
MOV AX,SEL_IDT
MOV ES,AX
XOR AL,AL
; Make all interrupt gates DPL=3
MOV AH,INTR_GATE OR DPL3
MOV CX,SEL_ICODE
MOV EDX,OFFSET IDTBEG
XOR SI,SI
MOV EBX,OFFSET INT0
IDTLOOP: CALL32F SEL_CODE32,MAKE_GATE
ADD EBX,INT1-INT0
ADD SI,8
; loop form max # of interrupts
CMP SI,(TOPINT+1)*8
JB SHORT IDTLOOP
LIDT NEWIDT
POP ES
RET
MAKIDT ENDP
; This routine is just like the real mode make_desc
; EBX=base ECX=limit AH=ARB AL=0 or 1 for 16 or 32 bit
; SI=selector (TI&RPL ignored) and ES:EDX is the table base address
MAKE_SEG PROC FAR
PUSH ESI
PUSH EAX
PUSH ECX
MOVZX ESI,SI
SHR SI,3 ; adjust to slot #
SHL AL,6 ; shift size to right bit position
CMP ECX,0FFFFFH ; see if you need to set G bit
JLE OKLIM
SHR ECX,12 ; div by 4096
OR AL,80H ; set G bit
OKLIM: MOV ES:[EDX+ESI*8],CX
SHR ECX,16
OR CL,AL
MOV ES:[EDX+ESI*8+6],CL
MOV ES:[EDX+ESI*8+2],BX
SHR EBX,16
MOV ES:[EDX+ESI*8+4],BL
MOV ES:[EDX+ESI*8+5],AH
MOV ES:[EDX+ESI*8+7],BH
POP ECX
POP EAX
POP ESI
RET
MAKE_SEG ENDP
; This routine make gates -- AL=WC if applicable -- AH=ARB -- EBX=offset
; CX=selector -- ES:EDX=table base -- SI= selector (TI&RPL ignored)
MAKE_GATE PROC FAR
PUSH ESI
PUSH EBX
SHR SI,3
MOVZX ESI,SI
MOV ES:[EDX+ESI*8],BX
MOV ES:[EDX+ESI*8+2],CX
MOV ES:[EDX+ESI*8+4],AX
SHR EBX,16
MOV ES:[EDX+ESI*8+6],BX
POP EBX
POP ESI
RET
MAKE_GATE ENDP
; Routine to call BIOS/DOS. NOT REENTRANT (but so what? DOS isn't either)
CALL86 PROC FAR
PUSH DS
PUSH GS
PUSH FS
RETRY86:
PUSHAD
PUSHFD
PUSH ES:[EBX+40] ; save new ebx
PUSH EBX
PUSH ES
INT 30H ; call PROT
PUSH SEL_DATA
POP DS
POP ES
XCHG EBX,[ESP]
POP ES:[EBX+40]
PUSHFD
CMP BREAKKEY,0 ; see if break occured
JZ SHORT NOBRKCHECK
CMP BRK_SEG,0 ; see if user has brk handler
JZ SHORT NOBRKCHECK
; call user's break handler
MOV BREAKKEY,0
CALL FWORD PTR BREAK_HANDLE
NOBRKCHECK:
CMP CRITICAL,0 ; see if critical error
JZ SHORT NOCRITCK
CMP CRIT_SEG,0 ; see if critical error handler
JZ SHORT NOCRITCK
; call critical error handler
PUSH EAX
XOR AL,AL
MOV CRITICAL,AL
CALL FWORD PTR CRIT_HANDLE
OR AL,AL ; AL=0? FAIL
JNZ SHORT RETRY?
POP EAX
POPFD
STC ; make sure carry is set
PUSHFD
JMP SHORT NOCRITCK
RETRY?: DEC AL ; AL=1? RETRY
JNZ SHORT CABORT
; To retry an error, we set up everything the way it was and
; redo the interrupt. This is cheating (a little), and may not
; work in every possible case, but it seems to work in all the cases tried.
POP EAX
POPFD
POP ES:[EBX+40]
POPFD
POPAD
JMP SHORT RETRY86
CABORT: POP EAX ; ABORT
POPFD
LEA ESP,[ESP+40] ; balance stack
MOV AL,7FH ; DOS error=7FH
BACK2DOS
NOCRITCK:
POPFD
LEA ESP,[ESP+40] ; balance stack
PUSHFD
; see if segment save requested
CMP BYTE PTR ES:[EBX],0
JZ NOSEGS
; load parameter block from static save area
PUSH EAX
MOV EAX,SAV_FS
MOV ES:[EBX+28],EAX
MOV EAX,SAV_DS
MOV ES:[EBX+24],EAX
MOV EAX,SAV_ES
MOV ES:[EBX+20],EAX
MOV EAX,SAV_GS
MOV ES:[EBX+32],EAX
POP EAX
NOSEGS:
POPFD
POP FS
POP GS
POP DS
MOV EBX,ES:[EBX+40]
RET
CALL86 ENDP
; Directly clear page 0 of the screen
CLS PROC FAR
PUSHFD
PUSH DS
PUSH ES
PUSH EDI
PUSH ECX
PUSH EAX
MOV CX,SEL_VIDEO
MOV ES,CX
MOV CX,SEL_DATA
MOV DS,CX
CLD
MOV EDI,0
MOV ECX,2000
MOV AX,0720H
REP STOSW
XOR ECX,ECX
MOV CURSOR,ECX
POP EAX
POP ECX
POP EDI
POP ES
POP DS
POPFD
RET
CLS ENDP
; Outputs message to screen -- ASCIIZ pointer in ds:ebx - modifies ebx
MESSOUT PROC FAR
PUSH EAX
NXT: MOV AL,[EBX]
INC EBX
OR AL,AL
JNZ SHORT SKIP
POP EAX
RET
SKIP: CALL32F SEL_CODE32, OUCH
JMP SHORT NXT
MESSOUT ENDP
; Performs CR/LF sequence to screen using OUCH
CRLF PROC FAR
PUSH EAX
MOV AL,13
CALL32F SEL_CODE32,OUCH
MOV AL,10
CALL32F SEL_CODE32,OUCH
POP EAX
RET
CRLF ENDP
; Character and digit output routines
; hexout4 - print longword in EAX in hex
; hexout2 - print word in AX in hex
; hexout - print byte in AL in hex
; ouch - print ASCII character in AL
OUTPUT PROC FAR
; print longword in eax
HEXOUT4 LABEL FAR
PUSH EAX
SHR EAX,16
CALL32F SEL_CODE32,HEXOUT2
POP EAX
; print word in ax
HEXOUT2 LABEL FAR
PUSH EAX
MOV AL,AH
CALL32F SEL_CODE32, HEXOUT
POP EAX
; print a hex byte in al
HEXOUT LABEL FAR
MOV BL,AL
AND AX,0F0H
SHL AX,4
MOV AL,BL
AND AL,0FH
ADD AX,'00'
MOV BL,AL
MOV AL,AH
CALL32F SEL_CODE32, HEX1DIG
MOV AL,BL
HEX1DIG: CMP AL,'9'
JBE SHORT H1DIG
ADD AL,'A'-'0'-0AH
H1DIG:
OUCH LABEL FAR
PUSH EDI
PUSH EAX
PUSH DS
PUSH ES
PUSH ECX
MOV CX,SEL_VIDEO
MOV ES,CX
MOV CX,SEL_DATA
MOV DS,CX
POP ECX
MOV AH,COLOR
MOV EDI,CURSOR
CMP EDI,2000 ; rolling off the screen?
JB NOSCROLL
; scroll screen if required
PUSH DS
PUSH ES
POP DS
PUSH ESI
PUSH ECX
PUSH EDI
CLD
MOV ECX,960
XOR EDI,EDI
MOV ESI,160
REP MOVSD
POP EDI
SUB EDI,80
POP ECX
POP ESI
POP DS
NOSCROLL: CMP AL,0DH
JZ SHORT CR
CMP AL,0AH
JZ SHORT LF
; write to screen
MOV ES:[EDI*2],AX
INC EDI
JMP SHORT OUCHD
CR: PUSH EDX
PUSH ECX
MOV EAX,EDI
XOR EDX,EDX
MOV ECX,80
DIV ECX
SUB EDI,EDX
POP ECX
POP EDX
JMP SHORT OUCHD
LF: ADD EDI,50H
OUCHD: MOV CURSOR,EDI ; update cursor
POP ES
POP DS
POP EAX
POP EDI
RET
OUTPUT ENDP
; Default critical error handler
DEF_CRIT PROC FAR
PUSH ES
PUSH EBX
PUSH EDX
MOV BX,SEL_DATA
MOV ES,BX
ASSUME DS:NOTHING, ES:DAT32
; load critical error handler's private stack
MOV BX,CSTACK
MOV CINTFRAME.VMSS,EBX
MOV EBX,OFFSET CSTACK
MOV CINTFRAME.VMESP,EBX
MOV BX,DAT32
MOV CINTFRAME.VMDS,EBX
MOV BX,21H
MOV CINTFRAME.VMINT,EBX
MOV EBX, OFFSET CINTFRAME
MOV EDX,OFFSET CRITMSG
MOV AH,9
PUSH EBX
VM86CALL ; print message
POP EBX
CLOOP:
MOV AH,7
PUSH EBX
VM86CALL ; get keystroke
POP EBX
; ignore function keys
OR AL,AL
JZ SHORT CRITFNKEY
MOV AH,AL
OR AL,20H ; convert to lower case
CMP AL,'a'
JNZ SHORT CFAIL?
MOV AL,2
JMP SHORT CREXIT
CFAIL?: CMP AL,'f'
JNZ SHORT CRETRY?
XOR AL,AL
JMP SHORT CREXIT
CRETRY?:
CMP AL,'r'
MOV AL,1
JNZ SHORT CRITBAD
CREXIT: MOV DL,AH ; echo letter + CRLF
MOV AH,2
PUSH EAX
PUSH EBX
VM86CALL
POP EBX
MOV AH,2
MOV DL,0DH
PUSH EBX
VM86CALL
POP EBX
MOV AH,2
MOV DL,0AH
VM86CALL
POP EAX
POP EDX
POP EBX
POP ES
RET
CRITFNKEY:
MOV AH,7
PUSH EBX
VM86CALL ; ignore fn key/alt-key
POP EBX
CRITBAD:
MOV DL,7
MOV AH,2
PUSH EBX
VM86CALL ; unknown input - ring bell
POP EBX
JMP SHORT CLOOP
DEF_CRIT ENDP
SEG32END EQU $
SEG32 ENDS
;********************************************************************
; user program - PROT includes the file defined by the variable PROGRAM.
; convoluted method to make MASM take a string equate for an include filename
TEMPINCLUDE MACRO FN ; ; temporary macro
INCLUDE &FN&.PM
ENDM
TEMPINCLUDE %PROGRAM
PURGE TEMPINCLUDE ; delete macro
; task state segments
INCLUDE TSS.INC
; 16 bit code (DOS entry/exit)
INCLUDE CODE16.INC
; Segment to determine the last memory address
ZZZSEG SEGMENT PARA PUBLIC 'ZZZ' USE16
ZZZSEG ENDS
ELSE
IF2
%OUT You must specify a program title
%OUT use: MASM /DPROGRAM=PNAME PROT.ASM...
ENDIF
.ERR
ENDIF
END ENTRY
[LISTING TWO]
;********************************************************************
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams -- All rights reserved. *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* This file is: GDT.INC, the Global Descriptor Table definitions. *
;********************************************************************
; See EQUMAC.INC for an explanation of the DESC macro
GDTSEG SEGMENT PARA PUBLIC 'CODE32' USE32
GDT EQU $ ; GDT space
DESC SEL_NULL ; DUMMY NULL SELECTOR
DESC SEL_CODE16 ; 16 BIT CODE SEGMENT
DESC SEL_DATA0 ; 4GB SEGMENT
DESC SEL_CODE32 ; 32 BIT CODE SEGMENT
DESC SEL_STACK ; 32 BIT STACK
DESC SEL_RDATA ; REAL MODE LIKE DATA SEG
DESC SEL_GDT ; GDT ALIAS
DESC SEL_VIDEO ; VIDEO MEMORY
DESC SEL_DATA ; 32 BIT DATA
DESC SEL_IDT ; IDT ALIAS
DESC SEL_ICODE ; ISR SEGMENT
DESC SEL_TSS0 ; DUMMY TASK BLOCK
DESC TSS0 ; SAME (MUST FOLLOW SEL_TSS0)
DESC SEL_TSS1 ; MAIN TASK BLOCK
DESC TSS1 ; SAME (MUST FOLLOW SEL_TSS1)
DESC SEL_UCODE ; USER CODE
DESC SEL_UDATA ; USER DATA
DESC SEL_PSP ; DOS PSP
DESC SEL_FREE ; FREE DOS MEMORY
DESC SEL_EXT ; EXTENDED MEMORY
DESC SEL_ENV ; ENVIROMENT
GDTEND = $
GDTSEG ENDS
[LISTING THREE]
;********************************************************************
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams -- All rights reserved. *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* This file is: EQUMAC.INC, assorted macros and equates. *
;********************************************************************
; EQUates the user may wish to change
ATCLASS EQU 1 ; 1=AT/386 0=INBOARD 386/PC
DOSSTACK EQU 200H ; stack size for DOS startup
VM86STACK EQU 200H ; stack size for VM86 int calls
CRITSTACK EQU 30H ; stack size for crit err handler
PMSTACK EQU 1000H ; stack size for p-mode stack
PVSTACK EQU 260 ; pl0/vm86 psuedo stack size
; Maximum protected mode interrupt # defined
TOPINT EQU 30H
; The critical error handler works different for DOS 2.X than for other DOS
; versions. In 99% of the cases it won't make any difference if you compile
; with DOS=2.... major dos version number (2, 3 or 4)
DOS EQU 3
; parameter block to interface for int 30H (call86 & VM86CALL)
VM86BLK STRUC
VMSEGFLAG DD 0 ; restore segment registers (flag)
VMINT DD 0 ; interrupt number
VMFLAGS DD 0 ; EFLAGS
VMESP DD 0 ; ESP
VMSS DD 0 ; SS
VMES DD 0 ; ES
VMDS DD 0 ; DS
VMFS DD 0 ; FS
VMGS DD 0 ; GS
VMEBP DD 0 ; EBP
VMEBX DD 0 ; EBX
VM86BLK ENDS
; Access rights equates. Use these with make_desc or make_seg
RO_DATA EQU 90H ; r/o data
RW_DATA EQU 92H ; r/w data
RO_STK EQU 94H ; r/o stack
RW_STK EQU 96H ; r/w stack
EX_CODE EQU 98H ; exec only code
ER_CODE EQU 9AH ; read/exec code
CN_CODE EQU 9CH ; exec only conforming code
CR_CODE EQU 9EH ; read/exec conforming code
LDT_DESC EQU 82H ; LDT entry
TSS_DESC EQU 89H ; TSS entry
; use these with make_gate
CALL_GATE EQU 8CH ; call gate
TRAP_GATE EQU 8FH ; trap gate
INTR_GATE EQU 8EH ; int gate
TASK_GATE EQU 85H ; task gate
; dpl equates
DPL0 EQU 0
DPL1 EQU 20H
DPL2 EQU 40H
DPL3 EQU 60H
; macro definitons
; other macros use this to error check parameters
; Give an error if last is blank or toomany is not blank
ERRCHK MACRO LAST,TOOMANY
IFNB <TOOMANY>
IF2
%OUT Too many parameters
ENDIF
.ERR
ENDIF
IFB <LAST>
IF2
%OUT Not enough parameters
ENDIF
.ERR
ENDIF
ENDM
; Perform absolute 16 bit jump (in a 16 bit segment)
JMPABS MACRO A,B,ERRCK
ERRCHK B,ERRCK
DB 0EAH ; ; absoulte 16 bit jump
DW OFFSET B
DW A
ENDM
; Peform absolute 32 bit jump (in a 32 bit segment)
JMPABS32 MACRO A,B,ERRCK
ERRCHK B,ERRCK
DB 0EAH ; ; absolute 32 bit jump
DD OFFSET B
DW A
ENDM
; this generates a correct 32 bit offset for a proc call
; since MASM doesn't sign extend 32 bit relative items
CALL32S MACRO LBL,ERRCK ; ; short call
ERRCHK LBL,ERRCK
DB 0E8H
DD LBL-($+4)
ENDM
CALL32F MACRO SG,LBL,ERRCK ; ; far call
ERRCHK LBL,ERRCK
DB 9AH
DD OFFSET LBL
DW SG
ENDM
JMP32S MACRO LBL,ERRCK ; ; short jump
ERRCHK LBL,ERRCK
DB 0E9H
DD LBL-($+4)
ENDM
; jcc32 uses condition codes used in Intel literature conditional jump macro
JCC32 MACRO CONDX,LBL,ERRCK
ERRCHK LBL,ERRCK
DB 0FH
IFIDNI <CONDX>,<A>
DB 87H
ELSEIFIDNI <CONDX>,<NBE>
DB 87H
ELSEIFIDNI <CONDX>, <AE>
DB 83H
ELSEIFIDNI <CONDX>, <C>
DB 82H
ELSEIFIDNI <CONDX>, <NAE>
DB 82H
ELSEIFIDNI <CONDX>, <B>
DB 82H
ELSEIFIDNI <CONDX>, <BE>
DB 86H
ELSEIFIDNI <CONDX>, <E>
DB 84H
ELSEIFIDNI <CONDX>, <Z>
DB 84H
ELSEIFIDNI <CONDX>, <G>
DB 8FH
ELSEIFIDNI <CONDX>, <GE>
DB 8DH
ELSEIFIDNI <CONDX>, <L>
DB 8CH
ELSEIFIDNI <CONDX>, <LE>
DB 8EH
ELSEIFIDNI <CONDX>, <NA>
DB 86H
ELSEIFIDNI <CONDX>, <NB>
DB 83H
ELSEIFIDNI <CONDX>, <NC>
DB 83H
ELSEIFIDNI <CONDX>, <NGE>
DB 8CH
ELSEIFIDNI <CONDX>, <NL>
DB 8DH
ELSEIFIDNI <CONDX>, <NO>
DB 81H
ELSEIFIDNI <CONDX>, <NP>
DB 8BH
ELSEIFIDNI <CONDX>, <NS>
DB 89H
ELSEIFIDNI <CONDX>, <NZ>
DB 85H
ELSEIFIDNI <CONDX>, <O>
DB 80H
ELSEIFIDNI <CONDX>, <P>
DB 8AH
ELSEIFIDNI <CONDX>, <PE>
DB 8AH
ELSEIFIDNI <CONDX>, <PO>
DB 8BH
ELSEIFIDNI <CONDX>, <S>
DB 88H
ELSE
%OUT JCC32: Unknown condition code
.ERR
ENDIF
DD LBL-($+4)
ENDM
; Override default operand size
OPSIZ MACRO NOPARM ; ; op size overide
ERRCHK X,NOPARM
DB 66H
ENDM
; Override default address size
ADSIZ MACRO NOPARM ; ; address size overide
ERRCHK X,NOPARM
DB 67H
ENDM
; delay macro for interrupt controller access
IDELAY MACRO NOPARM
LOCAL DELAY1,DELAY2
ERRCHK X,NOPARM
JMP SHORT DELAY1
DELAY1: JMP SHORT DELAY2
DELAY2:
ENDM
; BREAKPOINT MACROS
; MACRO to turn on NBREAKPOINTS. If used with no arguments (or a 1), this
; macro makes NBREAKPOINT active if used with an argument > 1, NBREAKPOINT
; will break after that many passes
BREAKON MACRO ARG,ERRCK
ERRCHK X,ERRCK
PUSH DS
PUSH SEL_DATA
POP DS
PUSH EAX
IFB <ARG>
MOV AL,1
ELSE
MOV AL,&ARG
ENDIF
MOV BPON,AL
POP EAX
POP DS
ENDM
; Turns off NBREAKPOINT
BREAKOFF MACRO NOPARAM
ERRCHK X,NOPARAM
PUSH DS
PUSH SEL_DATA
POP DS
PUSH EAX
XOR AL,AL
MOV BPON,AL
POP EAX
POP DS
ENDM
BREAKPOINT MACRO NOPARM
ERRCHK X,NOPARM
INT 3
ENDM
; Counter breakpoint - use BREAKON to set count control
; BREAKPOINT with memory dump.
; usage: BREAKDUMP seg_selector, offset, number_of_words
BREAKDUMP MACRO SEG,OFF,CNT,ERRCK
ERRCHK CNT,ERRCK
PUSH EAX
PUSH DS
MOV AX,SEL_DATA
MOV DS,AX
MOV AX,&SEG
MOV DUMP_SEG,AX
MOV EAX,OFFSET &OFF
MOV DUMP_OFF,EAX
MOV EAX,&CNT
MOV DUMP_CNT,EAX
POP DS
POP EAX
BREAKPOINT
ENDM
NBREAKDUMP MACRO SEG,OFF,CNT,ERRCK
ERRCHK CNT,ERRCK
LOCAL NONBP
PUSH DS
PUSH SEL_DATA
POP DS
PUSHFD
OR BPON,0
JZ NONBP
DEC BPON
JNZ NONBP
POPFD
POP DS
BREAKDUMP SEG,OFF,CNT
NONBP:
POPFD
POP DS
ENDM
; determine linear address of first free byte of memory (to nearest paragraph)
LOADFREE MACRO REG,ERRCK
ERRCHK REG,ERRCK
XOR E®,E®
MOV ®,SEG ZZZGROUP
SHL E®,4
ENDM
; Set up PINTFRAME (uses eax). Loads vmstack & vmdata to the ss:esp and
; ds slots in pintframe -- default ss:esp=ssint1 -- default ds=userdata
PROT_STARTUP MACRO VMSTACK,VMDATA,ERRCK
ERRCHK X,ERRCK
IFB <VMSTACK>
MOV AX,SEG SSINT1
ELSE
MOV AX,SEG VMSTACK
ENDIF
MOV PINTFRAME.VMSS,EAX
IFB <VMSTACK>
MOV EAX, OFFSET SSINT1
ELSE
MOV EAX, OFFSET VMSTACK
ENDIF
MOV PINTFRAME.VMESP,EAX
IFB <VMDATA>
MOV AX,SEG USERDATA
ELSE
MOV AX,SEG VMDATA
ENDIF
MOV PINTFRAME.VMDS,EAX
ENDM
; start PROT user segments
PROT_CODE MACRO NOPARM
ERRCHK X,NOPARM
USERCODE SEGMENT PARA PUBLIC 'CODE32' USE32
USERCODEBEG EQU $
ASSUME CS:USERCODE, DS:USERDATA, ES:DAT32
ENDM
PROT_DATA MACRO NOPARM
ERRCHK X,NOPARM
USERDATA SEGMENT PARA PUBLIC 'DATA32' USE32
USERDATABEG EQU $
ENDM
PROT_CODE_END MACRO NOPARM
ERRCHK X,NOPARM
USERCODEEND EQU $
USERCODE ENDS
ENDM
PROT_DATA_END MACRO NOPARM
ERRCHK X,NOPARM
USERDATAEND EQU $
USERDATA ENDS
ENDM
; Simplfy programs with no data segment
NODATA MACRO NOPARM
ERRCHK X,NOPARM
PROT_DATA
PROT_DATA_END
ENDM
; Mnemonic for call86 call
VM86CALL MACRO NOPARM
ERRCHK X,NOPARM
CALL32F SEL_CODE32,CALL86
ENDM
; Mnemonic for dos return
BACK2DOS MACRO RC,ERRCK
ERRCHK X,ERRCK
IFNB <RC>
MOV AL,RC
ENDIF
JMPABS32 SEL_CODE16,BACK16
ENDM
; Variables and macro to create GDT/LDT/IDT entries
C_GDT = 0
C_LDT = 0
C_IDT = 0
; create "next" descriptor with name in table. If no table specified, use GDT
DESC MACRO NAME,TABLE,ERRCK
DQ 0
IFB <TABLE>
NAME = C_GDT
C_GDT = C_GDT+8
ELSE
IFIDNI <TABLE>,<LDT>
; For LDT selectors, set the TI bit to one
NAME = C_&TABLE OR 4
ELSE
NAME = C_&TABLE
ENDIF
C_&TABLE = C_&TABLE+8
ENDIF
ENDM
[LISTING FOUR]
;********************************************************************
;* *
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams *
;* All rights reserved. *
;* *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* *
;* This file is: STACKS.INC, which contains all the stack segments. *
;* *
;********************************************************************
; 16 bit stack segment (for CODE16)
SSEG SEGMENT PARA STACK 'STACK' USE16
SSEG0 DB DOSSTACK DUP (?)
SSEG1 EQU $
SSEG ENDS
; 16 bit stack segment for vm86 int (both hardware & INT 30)
SSINT SEGMENT PARA STACK 'STACK' USE16
SSINT0 DB VM86STACK DUP (?)
SSINT1 EQU $
SSINT ENDS
; private stack for default critical error handler dos calls
CSTACK SEGMENT PARA STACK 'STACK' USE16
DB CRITSTACK DUP (?)
CSTACK ENDS
; 32 bit stack segment
SS32 SEGMENT PARA PUBLIC 'STACK' USE32
SSEG32 DB PMSTACK DUP (?)
SSEG321 EQU $
SS32 ENDS
[LISTING FIVE]
;********************************************************************
;* *
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams *
;* All rights reserved. *
;* *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* *
;* This file is: INT386.INC *
;* *
;********************************************************************
; Peculiarities
; 1 - We don't emulate lock, IRETD, PUSHFD, POPFD yet
; 2 - When calling INT 25 or INT 26 from protected mode
; flags are destroyed (not left on stack as in VM86, real mode)
; 3 - For now I don't support adding offsets to the return address
; on your vm86 stack to change where IRET goes to. That could be
; fixed, but I don't know of any PC software that does that
; fake segment for far ret interrupts
; (this segment has no descriptor in GDT/LDT)
QISR SEGMENT PARA PUBLIC 'CODE16' USE16
ASSUME CS:QISR
; push sacrifical words for IRET to eat.
; PL0 stack controls return anyway
QIRET:
PUSH 0
PUSH 0
PUSH 0
IRET
QISR ENDS
; IDT segment
IDTABLE SEGMENT PARA PUBLIC 'DATA32' USE32
IDTBEG EQU $
DQ TOPINT+1 DUP (0)
IDTEND EQU $
IDTABLE ENDS
;ISR segment
DEFINT MACRO N
INT&N LABEL FAR
PUSH &N
JMP NEAR PTR INTDUMP
ENDM
ISR SEGMENT PARA PUBLIC 'CODE32' USE32
ASSUME CS:ISR
ISRBEG EQU $
; This code defines interrupt handlers from 0 to TOPINT
; (TOPINT is defined in EQUMAC.INC)
INTNO = 0
REPT TOPINT+1
DEFINT %INTNO
INTNO = INTNO + 1
ENDM
; Debug dump messages
MESSAREA DB 'INT=',0
STKM DB 'Stack Dump:',0
TASKM DB ' TR=',0
RTABLE DB 'G'
DB 'F'
DB 'D'
DB 'E'
GTABLE DB 'DISIBPSPBXDXCXAX'
MEMMESS DB 'Memory Dump:',0
; All interrupts come here
; We check for the interrupt # pushed on the stack and
; vector accordingly. This adds some interrupt latency,
; but simplifies IDT construction.
INTDUMP LABEL NEAR
; check for GP error
CMP BYTE PTR [ESP],0DH
JZ NEAR PTR INT13H
NOT13:
; check for vm86 psuedo-int
CMP BYTE PTR [ESP],30H
JZ NEAR PTR INT30H
; hardware interrupt?
CMP BYTE PTR [ESP],20H
JB SHORT NOTIO
IF ATCLASS
CMP BYTE PTR [ESP],2FH
ELSE
CMP BYTE PTR [ESP],27H
ENDIF
JA SHORT NOTIO
JMP NEAR PTR HWINT
NOTIO:
; if we made it here, we have an unexpected interrupt
; so crank out a debug dump and exit to dos
PUSHAD
PUSH GS
PUSH FS
PUSH DS
PUSH ES
MOV AX,SEL_VIDEO
MOV ES,AX
MOV AX,CS
MOV DS,AX
; do dump
MOV ECX,4
INTL1:
MOV AL,[RTABLE-1+ECX]
CALL32F SEL_CODE32,OUCH
MOV AL,'S'
CALL32F SEL_CODE32,OUCH
MOV AL,'='
CALL32F SEL_CODE32,OUCH
POP EAX
CALL32F SEL_CODE32,HEXOUT2
PUSH ECX
MOV ECX,6
LSP1: MOV AL,' '
CALL32F SEL_CODE32,OUCH
LOOP LSP1
POP ECX
LOOP INTL1
CALL32F SEL_CODE32,CRLF
XOR ECX,ECX
INTL2: CMP CL,5
JNZ SHORT NOCRINT
CALL32F SEL_CODE32,CRLF
NOCRINT:
MOV AL,'E'
CALL32F SEL_CODE32,OUCH
MOV AL,[GTABLE+ECX*2]
CALL32F SEL_CODE32,OUCH
MOV AL,[GTABLE+1+ECX*2]
CALL32F SEL_CODE32,OUCH
MOV AL,'='
CALL32F SEL_CODE32,OUCH
POP EAX
CALL32F SEL_CODE32,HEXOUT4
MOV AL,' '
CALL32F SEL_CODE32,OUCH
INC CL
CMP CL,8
JNE SHORT INTL2
MOV EBX,OFFSET MESSAREA
CALL32F SEL_CODE32,MESSOUT
POP EAX
CALL32F SEL_CODE32,HEXOUT
MOV EBX,OFFSET TASKM
CALL32F SEL_CODE32,MESSOUT
STR AX
CALL32F SEL_CODE32,HEXOUT2
CALL32F SEL_CODE32,CRLF
; stack dump
XOR EAX,EAX
MOV AX,SS
LSL EDX,EAX
JNZ SHORT INTABT
MOV EBX,OFFSET STKM
CALL32F SEL_CODE32,MESSOUT
XOR CL,CL
INTL3: CMP ESP,EDX
JAE SHORT INTABT
TEST CL,7
JNZ SHORT NOSCR
CALL32F SEL_CODE32,CRLF
NOSCR: POP EAX
CALL32F SEL_CODE32,HEXOUT4
INC CL
MOV AL,' '
CALL32F SEL_CODE32,OUCH
JMP SHORT INTL3
INTABT:
; Check for memory dump request
MOV AX,SEL_DATA
MOV DS,AX
ASSUME DS:DAT32
MOV AX,WORD PTR DUMP_SEG
OR AX,AX
JZ SHORT NOMEMDUMP
; come here to do memory dump
CALL32F SEL_CODE32,CRLF
PUSH DS
PUSH CS
POP DS
MOV EBX,OFFSET MEMMESS
CALL32F SEL_CODE32,MESSOUT
CALL32F SEL_CODE32,CRLF
POP DS
MOV AX,WORD PTR DUMP_SEG
MOV ES,AX
CALL32F SEL_CODE32,HEXOUT2
MOV AL,':'
CALL32F SEL_CODE32,OUCH
MOV EDX,DUMP_OFF
MOV EAX,EDX
CALL32F SEL_CODE32,HEXOUT4
MOV ECX,DUMP_CNT
DUMPLOOP:
MOV AL,' '
CALL32F SEL_CODE32,OUCH
MOV EAX,ES:[EDX] ; get word
CALL32F SEL_CODE32,HEXOUT4
ADD EDX,4
SUB ECX,4
JA SHORT DUMPLOOP
CALL32F SEL_CODE32,CRLF
NOMEMDUMP:
MOV AL,20H ; Send EOI signal
IF ATCLASS
OUT 0A0H,AL
ENDIF
OUT 20H,AL ; just in case hardware did it
MOV AL,7FH ; return 7f to DOS
BACK2DOS
; Here we check the GP fault
; if the mode isn't VM86 we do a debug dump
; Otherwise we try and emulate an instruction
; If the instruction isn't known, we do a debug dump
INT13H:
ADD ESP,4 ; balance stack (remove intno)
TEST [ESP+12],20000H
JZ SHORT SIM13A ; wasn't a vm86 interrupt!
ADD ESP,4 ; remove error code
PUSH EAX
PUSH EBX
PUSH DS
PUSH EBP
MOV EBP,ESP ; point to stack frame
ADD EBP,10H
MOV AX,SEL_DATA0
MOV DS,AX
MOV EBX,[EBP+4] ; get cs
AND EBX,0FFFFH
SHL EBX,4
ADD EBX,[EBP] ; get eip
XOR EAX,EAX ; al = OPCODE byte
; ah = # of bytes skipped over
; bit 31 of eax=1 if OPSIZ prefix
; encountered
JMP SHORT INLOOP
; set sign bit of eax if OPSIZ
FSET: OR EAX,80000000H
INLOOP: MOV AL,[EBX]
INC AH
INC EBX
CMP AL,66H ; opsize prefix
JZ SHORT FSET
; scan for instructions
CMP AL,9DH
JZ SHORT DOPOPF
CMP AL,9CH
JZ SHORT DOPUSHF
CMP AL,0FAH
JZ NEAR PTR DOCLI
CMP AL,0FBH
JZ NEAR PTR DOSTI
CMP AL,0CDH
JZ NEAR PTR DOINTNN
CMP AL,0CFH
JZ NEAR PTR DOIRET
CMP AL,0F0H
JZ NEAR PTR DOLOCK
; Whoops! What the $#$%$#! is that?
POP EBP
POP DS
POP EBX
POP EAX
SIM13:
PUSH 0 ; simulate error
SIM13A:
PUSH 13 ; simulate errno
JMP32S NOT13
;********************************************************************
; The following routines emulate VM86 instructions. Their conditions
; on entry are:
; eax[31]=1 iff opsiz preceeded instruction
; ah=count to adjust eip on stack
; al=instruction
; [EBX] next opcode byte
; ds: zerobase segment
; This routine emulates a popf
DOPOPF:
MOV BX, [EBP] ; fix IP
ADD BL,AH
ADC BH,0
MOV [EBP],BX
; get ss*10H, add esp fetch top of stack
MOVZX EBX,WORD PTR [EBP+10H]
SHL EBX,4
ADD EBX,[EBP+0CH]
MOVZX EAX,WORD PTR [EBX]
MOV EBX,[EBP+8] ; get his real flags
AND BX,07000H ; only preserve NT,IOPL
AND AX,08FFFH ; wipe NT,IOPL in new flags
OR EAX,EBX
MOV [EBP+8],EAX ; save his real flag image
MOV EBX,2
ADD [EBP+0CH],EBX
MOV EBX,0FFFEFFFFH
AND [EBP+8],EBX
POP EBP
POP DS
POP EBX
POP EAX
IRETD
; Routine to emulate pushf
DOPUSHF:
MOV BX, [EBP] ; Fix ip
ADD BL,AH
ADC BH,0
MOV [EBP],BX
MOV EAX,[EBP+8] ; get his flags
; get ss, add esp and "push" flags
MOVZX EBX,WORD PTR [EBP+10H]
SHL EBX,4
ADD EBX,[EBP+0CH]
MOV [EBX-2],AX
MOV EBX,2
; adjust stack
SUB [EBP+0CH],EBX
; mask out flag bits
MOV EBX,0FFFEFFFFH
AND [EBP+8],EBX
POP EBP
POP DS
POP EBX
POP EAX
IRETD
; Emulate CLI
DOCLI:
MOV BX, [EBP] ; Fix ip
ADD BL,AH
ADC BH,0
MOV [EBP],BX
MOV EAX,[EBP+8] ; get flags
OR EAX,20000H ; set vm, clr RF & IOPL
AND EAX,0FFFECDFFH
MOV [EBP+8],EAX ; replace flags
POP EBP
POP DS
POP EBX
POP EAX
IRETD
; Emulate STI
DOSTI:
MOV BX, [EBP] ; Fix ip
ADD BL,AH
ADC BH,0
MOV [EBP],BX
MOV EAX,[EBP+8] ; get flags
OR EAX,20200H ; set vm, clr RF & IOPL
AND EAX,0FFFECFFFH
MOV [EBP+8],EAX ; replace flags
POP EBP
POP DS
POP EBX
POP EAX
IRETD
; This routine emulates an INT nn instruction
DOINTNN:
PUSH EDX
PUSH ECX
; get ss
MOVZX EDX,WORD PTR [EBP+10H]
SHL EDX,4
; add esp
ADD EDX,[EBP+0CH]
; move flags, qsir address to vm86 stack & correct esp
; ... flags
MOV CX, [EBP+08H]
MOV [EDX-2],CX
MOV WORD PTR [EDX-4],SEG QIRET
MOV WORD PTR [EDX-6],OFFSET QIRET
SUB DWORD PTR [EBP+0CH],6
MOV CX, [EBP] ; ip
INC AH ; adjust ip by # of bytes to skip
ADD CL,AH
ADC CH,0
MOV [EBP],CX
; get tss alias (always directly above TSS in GDT)
STR DX ; get our task #
SUB DX,8 ; alias is one above
MOV ES,DX
MOV DX,SEL_DATA
MOV DS,DX
ASSUME DS:DAT32
; get pl0 esp from TSS & push to local stack
MOV EDX,INTSP
SUB EDX,4
MOV INTSP,EDX
MOV ECX,ES:[4] ; esp0
MOV [EDX],ECX
; get int vector
MOV DX,SEL_DATA0
MOV DS,DX
MOV ECX,ESP ; adjust stack for int 30H
ADD ECX,60
MOV ES:[4],ECX
; test for zero; if so called from int 30H
OR AH,AH
MOVZX EDX,AL
JZ SHORT FROM30
; otherwise get int vector from CS:EIP stream
MOVZX EDX,BYTE PTR [EBX]
MOV ECX,ESP
ADD ECX,24
MOV ES:[4],ECX ; adjust stack for non-int 30H
FROM30:
; interrupt vector*4 = VM86 interrupt vector address
SHL EDX,2
; try to clean up mess on stack
MOV AX,SEL_DATA
MOV DS,AX
MOV STO2,EDX
POP ECX
POP EDX
XCHG STO2,EDX
MOV STO1,ECX
MOV STO3,EBP
POP EBP
XCHG STO3,EBP
POP ECX
MOV BX,SEL_DATA
MOV DS,BX
MOV STO4,ECX
POP EBX
POP EAX
MOV CX,SEL_DATA0
MOV DS,CX
; copy segment registers & esp for vm86 int
PUSH [EBP+20H]
PUSH [EBP+1CH]
PUSH [EBP+18H]
PUSH [EBP+14H]
PUSH [EBP+10H]
PUSH [EBP+0CH]
MOV ECX,[EBP+08]
; push flags (with vm=1,iopl=0),cs, eip, rf=0
OR ECX,20000H
; clear iopl, rf, tf, if and push flags
AND ECX,0FFFECCFFH
PUSH ECX
; read new cs/ip from 8086 idt
; ... push CS
MOVZX ECX,WORD PTR [EDX+2]
PUSH ECX
; ... push IP
MOVZX ECX,WORD PTR [EDX]
PUSH ECX
MOV CX,SEL_DATA
MOV DS,CX
PUSH STO4
MOV ECX,STO1
MOV EDX,STO2
MOV EBP,STO3
POP DS
IRETD ; go on to vm86 land
; Emulate IRET instruction
DOIRET:
; vm86 stack
MOVZX EAX,WORD PTR[EBP+10H]
SHL EAX,4
ADD EAX,[EBP+0CH]
MOV EBX,[EAX] ; get cs:ip
; If top of stack=0:0 than a RETF or RETF 2 was detected
OR EBX,EBX
JZ SHORT FARRETINT
PUSH ECX
XOR ECX,ECX
; compare return address with QIRET
MOV CX, SEG QIRET
SHL ECX,16
MOV CX,OFFSET QIRET
CMP EBX,ECX
POP ECX
; if equal than "normal" IRET
JZ SHORT NORMIRET
; Not equal then that vm86 jerk is "faking" an IRET to pass control
; We must build a "fake" pl0 frame
; adjust sp
ADD DWORD PTR [EBP+0CH],6
; get ip
MOVZX EBX,WORD PTR [EAX]
MOV [EBP],EBX
; get cs
MOVZX EBX,WORD PTR [EAX+2]
MOV [EBP+4],EBX
; get new flags
MOVZX EBX,WORD PTR [EAX+4]
OR EBX,20000H ; set vm, clr RF & IOPL
AND EBX,0FFFECFFFH
MOV [EBP+8],EBX
POP EBP
POP DS
POP EBX
POP EAX
IRETD ; go on
; this means qiret caught a FAR RET instead of an IRET
; we must preserve our current flags!
FARRETINT:
MOV EAX,EBP
POP EBP
POP DS
PUSH EBP
PUSH EAX
MOV BX,DS
MOV AX,SEL_DATA
MOV DS,AX
MOV STO3,EBX
POP EBP ; ISR's ebp
MOV EAX,[EBP+0CH]
ADD EAX,6 ; skip pushes from qiret
MOV STO4,EAX
; get flags
MOV EAX,[EBP+08H]
MOV STO2,EAX
JMP SHORT NIRET
; This handles the "normal" case
NORMIRET:
MOV BX,[EAX+4] ; get flags
MOV EAX,EBP
POP EBP
POP DS
PUSH EBP
PUSH EAX
MOV AX,BX
MOV BX,DS
PUSH SEL_DATA
POP DS
MOV STO2,EAX
MOV STO3,EBX
POP EBP ; ISR's ebp
XOR EAX,EAX
MOV STO4,EAX
NIRET:
PUSH ESI
XOR ESI,ESI
OR DWORD PTR [EBP+28H],0
; if CS=0 then int 30H asked for segment save
JNZ SHORT V86IRET
MOV EAX,[EBP+14H]
MOV SAV_ES,EAX
MOV EAX,[EBP+18H]
MOV SAV_DS,EAX
MOV EAX,[EBP+1CH]
MOV SAV_FS,EAX
MOV EAX,[EBP+20H]
MOV SAV_GS,EAX
MOV ESI,8
V86IRET:
MOV AX,ES
MOV STO1,EAX
POP EBP
XCHG EBP,[ESP]
; get tss alias
STR AX
SUB AX,8
MOV ES,AX
ASSUME DS:DAT32
MOV EAX,ES:[4] ; get our current stack begin
; see if we have to balance the VM86 stack
TEST SS:[EAX+ESI+8],20000H
JZ SHORT STKADJD
MOV EBX,STO4
OR EBX,EBX
JZ SHORT ADJSTK
; balance vm86 stack
MOV SS:[EAX+ESI+0CH], EBX
JMP SHORT STKADJD
ADJSTK: ADD DWORD PTR SS:[EAX+ESI+0CH],6
STKADJD:
; get quasi flags
MOV EBX,STO2
; get real flags
PUSH SS:[EAX+ESI+8]
; preserve flags
MOV DWORD PTR SS:[EAX+ESI+8],EBX
LEAVEFLAGS:
; only let 8086 part of flags stay
AND DWORD PTR SS:[EAX+ESI+08],01FFFH
POP EBX ; load real flags into ebx
; save 386 portion of old flags (AND IP)
AND EBX,0FFFFE200H
OR SS:[EAX+ESI+8],EBX
POP ESI
XCHG EAX,[ESP]
PUSH EAX ; stack = ebx, new sp
MOV EBX,INTSP
; get prior pl0 esp from local stack
MOV EAX,[EBX]
ADD EBX,4
MOV INTSP,EBX
MOV ES:[4],EAX ; restore to TSS
; restore registers
POP EBX
MOV ES,WORD PTR STO1
MOV DS,WORD PTR STO3
POP EAX ; restore "real" eax
XCHG EAX,[ESP]
POP ESP ; set up new top stack
XCHG EAX,[ESP+4]
OR EAX,EAX ; test cs
XCHG EAX,[ESP+4]
JNZ SHORT GOIRET
ADD ESP,8 ; skip fake CS/IP from INT 30H
GOIRET:
; reset resume flag
AND DWORD PTR [ESP+8],0FFFECFFFH
IRETD
; Emulate lock prefix
DOLOCK:
POP EBP
POP DS
POP EBX
POP EAX
PUSH 0FFFFH
PUSH 13 ; simulate errno
JMP32S NOT13
; This is the interface routine to allow a protected mode
; program call VM86 interrupts.
; Call with es:ebx pointing to a parameter block
; +00 flag - if 1 then resave ES, DS, FS & GS
; into parameter block after call
; +04 int number (0-255) (required)
; +08 eflags
; +12 vm86 esp (required)
; +16 vm86 ss (required)
; +20 vm86 es
; +24 vm86 ds
; +28 vm86 fs
; +32 vm86 gs
; +36 vm86 ebp ( to replace that used in call )
; +40 vm86 ebx ( to replace that used in call )
;
; all other registers will be passed to vm86 routine
;
; This routine depends on the dointnn routine
INT30H:
ADD ESP,4 ; remove intno
CMP BYTE PTR ES:[EBX],0
JZ SHORT NOSEGSAV
; dummy CS/IP to signal IRET to save segments
PUSH 0
PUSH 0
NOSEGSAV:
PUSH ES:[EBX+32] ; stack up registers
PUSH ES:[EBX+28]
PUSH ES:[EBX+24]
PUSH ES:[EBX+20]
PUSH ES:[EBX+16]
PUSH ES:[EBX+12]
; force VM86=1 in EFLAGS
XCHG EAX,ES:[EBX+8]
OR EAX,20000H
AND EAX,0FFFECFFFH
PUSH EAX
XCHG EAX,ES:[EBX+8]
PUSH 0 ; don't care cs
PUSH 0 ; don't care eip
MOV EBP,ESP
PUSH EAX
PUSH ES:[EBX+40] ; vm86 ebx
PUSH DS
PUSH ES:[EBX+36] ; vm86 ebp
MOV AX,SEL_DATA0
MOV DS,AX
; get user's intno
MOV AL,ES:[EBX+4]
; set flag to dointnn not to check cs:ip for int #
MOV AH,0FFH
; go ahead.... make my interrupt
JMP32S DOINTNN
; handle hardware int!
; This routine uses INT 30 to handle HW interrupts
; If interrupted in protected mode, a special stack
; is used. If in VM86 mode, the current VM86 stack is used
HWINT:
XCHG EAX,[ESP] ; swap eax & int #
PUSH DS
PUSH ES
PUSH EBX
MOV BX,SEL_DATA
MOV DS,BX
MOV ES,BX
CMP EAX,28H
JB SHORT IRQ07
ADD EAX,48H ; vector IRQ8-F to INT 70-77
JMP SHORT IRQSET
IRQ07:
SUB EAX,24 ; vector IRQ0-7 to INT 8-0F
IRQSET:
; set up special interrupt frame
MOV HINTFRAME.VMINT,EAX
MOV HINTFRAME.VMEBP,EBP
POP EBX
MOV HINTFRAME.VMEBX,EBX
PUSH EBX
MOV EAX,020000H ; model flags
MOV HINTFRAME.VMFLAGS,EAX
MOV EAX,OFFSET SSINT1
MOV HINTFRAME.VMESP,EAX
MOV AX,SEG SSINT1
MOV HINTFRAME.VMSS,EAX
MOV EAX,[ESP+24] ; get flags
TEST EAX,20000H ; check vm
JZ SHORT NOTVMHW
MOV EAX,[ESP+28] ; get vm86's esp
MOV HINTFRAME.VMESP,EAX
MOV EAX,[ESP+32]
MOV HINTFRAME.VMSS,EAX
NOTVMHW:
MOV EBX,OFFSET HINTFRAME
PUSH FS
PUSH GS
INT 30H ; Do interrupt
POP GS
POP FS
POP EBX
POP ES
POP DS
POP EAX
IRETD
ISREND EQU $
ISR ENDS
[LISTING SIX]
;********************************************************************
;* *
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams *
;* All rights reserved. *
;* *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* *
;* This file is: TSS.INC, the Task State Segment definitions. *
;* *
;********************************************************************
; define TSS structure
; for more details refer to the Intel documentation
; remember, the defined values are only defaults, and
; can be changed when a value is defined
TSSBLK STRUC
BLINK DD 0
ESPP0 DD OFFSET SSEG321
SSP0 DD SEL_STACK
ESPP1 DD 0
SSP1 DD SEL_STACK
ESPP2 DD 0
SSP2 DD SEL_STACK
CR31 DD 0
EIP1 DD OFFSET USER
EF1 DD 200H
EAX1 DD 0
ECX1 DD 0
EDX1 DD 0
EBX1 DD 0
ESP1 DD OFFSET SSEG321
EBP1 DD 0
ESI1 DD 0
EDI1 DD 0
ES1 DD SEL_DATA
CS1 DD SEL_UCODE
SS1 DD SEL_STACK
DS1 DD SEL_UDATA
FS1 DD SEL_DATA0
GS1 DD SEL_VIDEO
LDT1 DD 0
DW 0
IOT DW $+2-OFFSET BLINK
IOP DB 8192 DUP (0)
DB 0FFH
TSSBLK ENDS
TSSSEG SEGMENT PARA PUBLIC 'DATA32' USE16
ORG 0
; Dummy TSS that stores the original machine state
TSS0BEG TSSBLK <>
TSS0END EQU $
; TSS to run the USER task
TSS1BEG TSSBLK <>
TSS1END EQU $
TSSSEG ENDS
[LISTING SEVEN]
;********************************************************************
;* *
;* PROT - A 386 protected mode DOS extender *
;* Copyright (C) 1989, by Al Williams *
;* All rights reserved. *
;* *
;* Permission is granted for non-commercial use of this software *
;* subject to certain conditions (see PROT.ASM). *
;* *
;* This file is: CODE16.INC, the 16 bit DOS entry/exit code. *
;* *
;********************************************************************
CSEG SEGMENT PARA PUBLIC 'CODE16' USE16
ASSUME CS:CSEG, DS:CSEG
BEG16 EQU $
IDTSAV DF 0 ; space to save old real mode IDT
XZRO DF 0 ; Zero constant for inhibiting IDT
TEMP EQU THIS FWORD ; Space to load GDT
TLIM DW (GDTEND-GDT)-1
TEMD DD 0
; area to save stack pointer
SOFFSAV DW 0
SSEGSAV DW 0
; old keyboard interrupt vector -- we have to catch reboot requests
KEYCHAIN EQU THIS DWORD
KEYOFF DW ?
KEYSEG DW ?
INTM DB 0 ; interrupt mask - pic 1
IF ATCLASS
INTMAT DB 0 ; interrupt mask - pic 2 (AT ONLY)
ENDIF
;psp
PSP DW 0
; error messages
NOT386M DB 'Error: this program requires an 80386 or 80486'
DB ' processor.',13,10,'$'
VM86M DB 'Error: this program will not execute '
DB 'in VM86 mode.'
DB 13,10,'$'
; 16 bit ss/sp for return to real mode
LOAD16 DD OFFSET SSEG1-1
DW SEL_RDATA
;****** Begin program
ENTRY LABEL FAR
START PROC NEAR
PUSH CS ; set up DS segment, save PSP
POP DS
MOV AX,ES
MOV PSP,AX ; save PSP
MOV BX,DAT32
MOV ES,BX
MOV ES:_PSP,AX
; check to see if we are running on a 386/486
XOR AX,AX
PUSH AX
POPF
PUSHF
POP AX
AND AX,0F000H
CMP AX,0F000H
JNZ SHORT NOT86
NOT386:
MOV DX, OFFSET NOT386M
NOT386EXIT:
MOV AH,9
INT 21H
MOV AX,4C80H
INT 21H ; exit
NOT86:
MOV AX,0F000H
PUSH AX
POPF
PUSHF
POP AX
AND AX,0F000H
JZ SHORT NOT386
; If we got here we are on an 80386.
; Check PM flag
SMSW AX
AND AX,1 ; are we in protected mode?
MOV DX,OFFSET VM86M
JNZ SHORT NOT386EXIT
; OK.. we are clear to proceed
; Set up new ^C, keyboard and Critical error handlers
MOV AX,3509H
INT 21H
MOV AX,ES
MOV KEYSEG,AX
MOV KEYOFF,BX
MOV AX,2509H
MOV DX,OFFSET REBOOT
INT 21H
MOV AX,2523H
MOV DX,OFFSET CTRLC
INT 21H
MOV AX,2524H
MOV DX,OFFSET CRITERR
INT 21H
; * Create segments
PUSH GDTSEG
POP ES
MOV EDX, OFFSET GDT
MOV EBX,CS
SHL EBX,4 ; calc segment base address
MOV ECX,0FFFFH ; 64 K limit (don't change)
MOV AH,ER_CODE ; read/exec code seg
XOR AL,AL ; size
PUSH GDTSEG
POP ES
MOV EDX, OFFSET GDT
MOV SI,SEL_CODE16
CALL MAKE_DESC ; make code seg (16 bit/real)
MOV ECX,0FFFFFH
XOR EBX,EBX
MOV SI,SEL_DATA0
XOR ECX,ECX
DEC ECX ; ecx=ffffffff
MOV AL,1
MOV AH,RW_DATA
CALL MAKE_DESC ; make data ( 4G @ zero base )
XOR EAX,EAX
INT 12H
MOVZX ECX,AX
SHL ECX,10
LOADFREE BX ; get free memory segment
SUB ECX,EBX
DEC ECX
MOV SI,SEL_FREE
MOV AL,1
MOV AH,RW_DATA
CALL MAKE_DESC
XOR EAX,EAX
MOV AH,88H ; get top of extended memory
INT 15H
SHL EAX,10 ; * 1024
OR EAX,EAX ; any extended present?
MOV ECX,EAX
JNZ SHORT EXTPRES
MOV ECX,1
EXTPRES:
DEC ECX
MOV EBX,100000H
MOV SI,SEL_EXT ; 0 limit segment if no ext.
MOV AL,1
MOV AH,RW_DATA
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,SEG SEG32ENT
SHL EBX,4
MOV ECX,(SEG32END-SEG32BEG)-1
MOV AH,ER_CODE
MOV AL,1
MOV SI,SEL_CODE32
CALL MAKE_DESC ; 32 bit code segment
XOR EBX,EBX
MOV BX,USERCODE
SHL EBX,4
MOV ECX,(USERCODEEND-USERCODEBEG)-1
MOV AH,ER_CODE
MOV AL,1
MOV SI,SEL_UCODE
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,USERDATA
SHL EBX,4
MOV ECX,(USERDATAEND-USERDATABEG)-1
MOV AH,RW_DATA
MOV AL,1
MOV SI,SEL_UDATA
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,SS32
SHL EBX,4 ; always para align stacks!
MOV ECX,(SSEG321-SSEG32)-1
MOV AH,RW_DATA ; stack seg is data type
MOV AL,1
MOV SI,SEL_STACK
CALL MAKE_DESC
XOR EBX,EBX
MOV BX, SSEG
SHL EBX,4
MOV ECX,0FFFFH ; real mode limit (don't change)
XOR AL,AL
MOV AH,RW_DATA
MOV SI,SEL_RDATA
CALL MAKE_DESC ; 16 bit data for return to r/m
XOR EBX,EBX
MOV BX,SEG GDT
SHL EBX,4
ADD EBX,OFFSET GDT
MOV ECX,(GDTEND-GDT)-1
MOV AL,1
MOV AH,RW_DATA
MOV SI,SEL_GDT
CALL MAKE_DESC
MOV AX,500H ; set video to page 0
INT 10H
MOV AH,0FH
INT 10H ; get mode
MOV EBX,0B0000H ; monochrome
CMP AL,7 ; check for mono
JZ SHORT VIDEOCONT
MOV EBX,0B8000H
VIDEOCONT:
MOV ECX,3999 ; limit for text page
MOV AL,1
MOV AH,RW_DATA
MOV SI,SEL_VIDEO
CALL MAKE_DESC ; make video segment
XOR EBX,EBX
MOV BX,DAT32
SHL EBX,4
MOV ECX,(DAT32END-DAT32BEG)-1
MOV AH,RW_DATA
MOV AL,1
MOV SI,SEL_DATA
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,IDTABLE
SHL EBX,4
MOV ECX,(IDTEND-IDTBEG)-1
MOV AH,RW_DATA
MOV AL,1
MOV SI,SEL_IDT
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,ISR
SHL EBX,4
MOV ECX,(ISREND-ISRBEG)-1
MOV AH,ER_CODE
MOV AL,1
MOV SI,SEL_ICODE
CALL MAKE_DESC
XOR EBX,EBX
MOV BX,TSSSEG
SHL EBX,4
; compute TSS length
MOV ECX,(TSS0END-TSS0BEG)-1
MOV AH,RW_DATA
MOV AL,1
MOV SI,SEL_TSS0
CALL MAKE_DESC
MOV AH,TSS_DESC
MOV SI,TSS0
CALL MAKE_DESC
ADD EBX,OFFSET TSS1BEG
MOV SI,TSS1
; compute TSS length
MOV ECX,(TSS1END-TSS1BEG)-1
CALL MAKE_DESC
MOV SI,SEL_TSS1
MOV AH,RW_DATA
CALL MAKE_DESC
MOVZX EBX,PSP
SHL EBX,4
MOV ECX,255
MOV AH,RW_DATA
MOV AL,1
MOV SI,SEL_PSP
CALL MAKE_DESC
PUSH ES
MOV AX,PSP
MOV ES,AX
XOR EBX,EBX
MOV BX,ES:[2CH]
MOV AX,BX
SHL EBX,4
DEC AX
MOV ES,AX
XOR ECX,ECX
MOV CX,ES:[3]
SHL ECX,4
DEC ECX ; get limit
MOV SI,SEL_ENV
POP ES
MOV AL,1
MOV AH,RW_DATA
CALL MAKE_DESC ; make envrioment segment
; turn on A20
MOV AL,1
CALL SETA20
CLI ; no interrupts until prot mode
MOV SSEGSAV,SS
; save sp for triumphant return to r/m
MOV SOFFSAV,SP
SIDT IDTSAV
LIDT XZRO ; save and load IDT
XOR EBX,EBX
MOV BX,SEG GDT
SHL EBX,4
ADD EBX,OFFSET GDT
MOV TEMD,EBX
LGDT TEMP ; set up GDT
MOV EAX,CR0
OR EAX,1 ; switch to prot mode!
MOV CR0,EAX
; jump to load CS and flush prefetch
JMPABS SEL_CODE16,PROT1
PROT1:
OPSIZ
JMPABS32 SEL_CODE32,SEG32ENT
; Jump here to return to real mode DOS.
; If desired AL can be set to a DOS exit code
BACK16 LABEL FAR
MOV BL,AL ; save exit code
CLI
XOR EAX,EAX
MOV DR7,EAX ; turn off debug (just in case)
; restore stack
LSS ESP,FWORD PTR CS:LOAD16
MOV AX,SEL_RDATA
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV EAX,CR0
; return to real mode
AND EAX,07FFFFFF2H
MOV CR0,EAX
; jump to load CS and clear prefetch
JMPABS CSEG,NEXTREAL
NEXTREAL LABEL FAR
MOV AX,CS
MOV DS,AX
LIDT IDTSAV ; restore old IDT 0(3ff)
IN AL,21H
MOV INTM,AL
; reprogram PIC's
IF ATCLASS
IN AL,0A1H
MOV INTMAT,AL
MOV AL,11H
OUT 0A0H,AL
OUT 20H,AL
IDELAY
MOV AL,70H
OUT 0A1H,AL
MOV AL,8
OUT 21H,AL
IDELAY
MOV AL,2
OUT 0A1H,AL
MOV AL,4
OUT 21H,AL
IDELAY
MOV AL,1
OUT 0A1H,AL
OUT 21H,AL
IDELAY
MOV AL,INTMAT
OUT 0A1H,AL
MOV AL,INTM
OUT 21H,AL
ELSE
MOV AL,13H
OUT 20H,AL
MOV AL,8
OUT 21H,AL
INC AL
OUT 21H,AL
MOV AL,INTM
OUT 21H,AL
ENDIF
; clean up to go back to DOS
LSS SP,DWORD PTR SOFFSAV
STI ; resume interupt handling
; turn a20 back off
XOR AL,AL
CALL SETA20
; restore keyboard interrupt
MOV DX,KEYOFF
MOV AX,KEYSEG
PUSH DS
MOV DS,AX
MOV AX,2509H
INT 21H
POP DS
MOV AH,4CH ; blow this joint!
MOV AL,BL ; get return code
INT 21H ; return to the planet of MSDOS
START ENDP
; Routine to control A20 line
; AL=1 to turn A20 on (enable)
; AL=0 to turn A20 off (disable)
; returns ZF=1 if error; AX destroyed
IF ATCLASS
SETA20 PROC NEAR
PUSH CX
MOV AH,0DFH ; A20 On
OR AL,AL
JNZ SHORT A20WAIT1
MOV AH,0DDH ; A20 Off
A20WAIT1:
CALL KEYWAIT
JZ SHORT A20ERR
MOV AL,0D1H
OUT 64H,AL
CALL KEYWAIT
MOV AL,AH
OUT 60H,AL
CALL KEYWAIT
JZ SHORT A20ERR
MOV AL,0FFH
OUT 64H,AL
CALL KEYWAIT
A20ERR: POP CX
RET
SETA20 ENDP
; Wait for keyboard controller ready. Returns ZF=1 if timeout
; destroys CX and AL
KEYWAIT PROC NEAR
XOR CX,CX ; maximum time out
KWAITLP:
DEC CX
JZ SHORT KEYEXIT
IN AL,64H
AND AL,2
JNZ KWAITLP
KEYEXIT: OR CX,CX
RET
KEYWAIT ENDP
ELSE
; INBOARD PC Code for A20
SETA20 PROC NEAR
OR AL,AL
MOV AL,0DFH
JNZ A20SET
MOV AL,0DDH
A20SET: OUT 60H,AL
OR AL,AL ; make sure ZF is set for
RET ; compatibilty with AT routines
SETA20 ENDP
ENDIF
; This routine makes a descriptor
; ebx=base
; ecx=limit in bytes
; es:edx=GDT address
; al= size (0=16bit 1=32bit)
; ah=AR byte
; SI=descriptor (TI & DPL not important!)
; Auto sets and calculates G and limit
MAKE_DESC PROC NEAR
PUSHAD
MOVZX ESI,SI
SHR SI,3 ; adjust to slot #
SHL AL,6 ; shift size to right bit position
CMP ECX,0FFFFFH ; see if you need to set G bit
JBE SHORT OKLIMR
SHR ECX,12 ; div by 4096
OR AL,80H ; set G bit
OKLIMR: MOV ES:[EDX+ESI*8],CX
SHR ECX,16
OR CL,AL
MOV ES:[EDX+ESI*8+6],CL
MOV ES:[EDX+ESI*8+2],BX
SHR EBX,16
MOV ES:[EDX+ESI*8+4],BL
MOV ES:[EDX+ESI*8+5],AH
MOV ES:[EDX+ESI*8+7],BH
POPAD
RET
MAKE_DESC ENDP
; This is the routine that disables ^C interrupts
; You could place your own code here if desired
; NOTE: THIS IS VM86 CODE!
CTRLC PROC FAR
PUSH DS
PUSH AX
MOV AX,DAT32
MOV DS,AX
ASSUME DS:DAT32
MOV AL,1
MOV BREAKKEY,AL ; set flag
POP AX
POP DS
IRET
CTRLC ENDP
; Reboot handler (VM86 code)
REBOOT PROC FAR
STI
PUSH AX
IN AL,60H
CMP AL,53H ; delete key?
JNZ SHORT NOREBOOT
XOR AX,AX
PUSH DS
MOV DS,AX
MOV AL,DS:[417H] ; get shift status
POP DS
TEST AL,8 ; check for cntl/alt
JZ SHORT NOREBOOT
TEST AL,4
JZ SHORT NOREBOOT
; If detected a ^ALT-DEL then eat it and return
IN AL,61H
MOV AH,AL
OR AL,80H
OUT 61H,AL
MOV AL,AH
OUT 61H,AL
MOV AL,20H
OUT 20H,AL
POP AX
IRET
; not a ^ALT-DEL, resume normal keyboard handler
NOREBOOT: POP AX
JMP CS:[KEYCHAIN]
REBOOT ENDP
; Critical error handler (always fail/ignore)
CRITERR PROC FAR
PUSH DS
PUSH DAT32
POP DS
ASSUME DS:DAT32
MOV CRITAX,AX
MOV AL,1
MOV CRITICAL,AL
MOV CRITDI,DI
MOV CRITBP,BP
MOV CRITSI,SI
IF DOS LT 3
XOR AL,AL
ELSE
MOV AL,3
ENDIF
POP DS
IRET
CRITERR ENDP
LAST16 EQU $
CSEG ENDS
[Example 1: A simple PROT program.]
File: USER.INC
; SET UP EMPTY DATA SEGMENT
NODATA
; SET UP CODE SEGMENT - PROGRAM RETURNS TO DOS
PROT_CODE
USER PROC NEAR
BACK2DOS
USER ENDP
PROT_CODE_END
[Example 2: DOS and PROT code fragments to print a message using
DOS service 9.]
Real Mode Program
REALPGM PROC
MOV AX,SEG STACKAREA
MOV SS,AX
MOV SP,OFFSET STACKAREA ; SET UP STACK
MOV AX,SEG DATSEG
MOV DS,AX ; SET UP DATA SEGMENT
MOV DX,OFFSET MESSAGE ; LOAD POINTER TO MESSAGE
MOV AH,9
INT 21H ; PRINT MESSAGE
MOV AH,4CH
INT 21H ; RETURN TO DOS
REALPGM ENDP
PROT Equivalent
USER PROC
PROT_STARTUP ; SET UP STACK/DS
MOV AX,21H
MOV PINTFRAME.VMINT,EAX
MOV EDX,OFFSET MESSAGE ; LOAD POINTER TO MESSAGE
MOV AH,9
MOV EBX,OFFSET PINTFRAME
VM86CALL ; PRINT MESSAGE
BACK2DOS ; RETURN TO DOS
USER ENDP
[Example 3: Maintaining the caller's flags on the stack when
returning in protected mode.]
MOV EAX,25H
MOV PINTFRAME.VMINT,EAX
PUSHF ; (Or PUSHFD)
VM86CALL ; Call INT 25 or 26
.
.
[Example 4: 32-bit offset generation problems]
.386P
SEGMENT EXAMPLE PARA 'CODE32' USE32
.
BACKWARD:
.
.
.
CMP EBX,EAX
JA FORWARD ; This jump is OK
JB BACKWARD ; This jump is improperly assembled
.
.
.
FORWARD:
[Example 5: QISR code for the VM86 mode segment
qisr segment para 'CODE16' use16]
assume cs:qisr
qiret:
push 0
push 0
push 0
iret
qisr ends
[Figure 1: Parameter block for call86 routine.]
Address Member name
BLOCK+0 |------------------------------------| VMSEGFLAG
| Segment register flag (see text) |
BLOCK+4 |------------------------------------| VMINT
| Interrupt number |
BLOCK+8 |------------------------------------| VMFLAGS
| EFLAGS |
BLOCK+12 |------------------------------------| VMESP
| ESP |
BLOCK+16 |------------------------------------| VMSS
| SS |
BLOCK+20 |------------------------------------| VMES
| ES |
BLOCK+24 |------------------------------------| VMDS
| DS |
BLOCK+28 |------------------------------------| VMFS
| FS |
BLOCK+32 |------------------------------------| VMGS
| GS |
BLOCK+36 |------------------------------------| VMEBP
| EBP |
BLOCK+40 |------------------------------------| VMEBX
| EBX |
|------------------------------------|
[Figure 2: Batch file used to compile a PROT program]
echo off
if X%1==X goto :errexit
if NOT X%2==X goto :errexit
masm /DPROGRAM=%1 PROT.ASM,%1.OBJ,%1.LST;
if ERRORLEVEL 1 goto :exit
link %1;
goto :exit
:errexit
echo PMASM - An MASM driver for the PROT 386 DOS Extender
echo usage: PMASM progname
echo Assembles the file progname.pm into progname.exe
echo The PROT system is copyright (C), 1989 by Al Williams.
echo Please see the file "PROT.ASM" for more details.
:exit
[Figure 3: PROT interrupt/breakpoint display]
ES=0040 DS=0090 FS=0010 GS=0038
EDI=00000000 ESI=00000000 EBP=00000000 ESP=00000FF0 EBX=00000000
EDX=00000000 ECX=00000000 EAX=00000000 INT=03 TR=0070
Stack Dump:
0000002B 00000088 00000202
[Figure 4: Problem cases associated with software interrupts]
Case 1: Normal INT/IRET
...
INT 10H ; perform interrupt
...
ISR: ; Interrupt 10H service routine
...
IRET
Case 2: INT/RETF 2
...
INT 10H ; perform interrupt
...
ISR: ; Interrupt 10H service routine
...
RETF 2
Case 3: INT/RETF (only used by INT 25H and 26H)
...
INT 10H ; perform interrupt
...
ISR: ; Interrupt 10H service routine
...
RETF
Case 4: PUSHF/FAR CALL
...
PUSHF ; simulate interrupt
CALL FAR ISR
...
ISR: ;Interrupt 10H service routine
...
IRET
Case 5: PUSHF/PUSH ADDRESS/IRET
...
PUSHF ; Jump to address TARGET
PUSH SEG TARGET
PUSH OFFSET TARGET
IRET
...
TARGET: ; Destination of IRET
...
-or-
PUSHF ; Simulate interrupt
PUSH SEG RETAD
PUSH OFFSET RETAD
JMP FAR ISR
RETAD:
...
ISR: ; Interrupt routine
...
IRET